﻿#region COPYRIGHT
//--------------------------------------------------------------------------------
// <copyright file="KeyboardTextInputTracker.cs" company="starLiGHT Entertainment Studios">
//    Copyright (c) 2011 
//       Roland Rosenkranz (Glatzemann@email.de)
// </copyright>
// <license>
//   This file is part of starLiGHT.GUI.
//
//   It is released under the Microsoft Public License (Ms-PL).
//
//   You should have received a copy of the Microsoft Public License
//   along with starLiGHT.GUI. If not, see http://sle.codeplex.com/license.
//    
//   ADDITIONAL (commercial) LICENSES for starLiGHT.GUI are available on request.
// </license>
// <version>
// **************[ starLiGHT.Engine SVN ]**********************
// * $Rev:: 9659                  $:  Revision of last commit *
// * $Author:: unknown            $:  Author of last commit   *
// * $Date:: 2011-05-04 12:33:30 #$:  Date of last commit     *
// ************************************************************
// </version>
//--------------------------------------------------------------------------------
#endregion

namespace starLiGHT.GUI.Input
{
    #region Using Statements
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Linq;
    using System.Text;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Graphics;
    using Microsoft.Xna.Framework.Input;
    #endregion

    public class KeyboardTextInputTracker : TextInputTracker
    {
        #region constants

        // the letter groups is an array of characters where the lower case letter is followed by it's upper case partner
        private const string ShiftLetterGroupsDE = "0=1!2\"3§4$5%6&7/8(9)^°ß?´`+*#'-_.:,;<>öÖäÄüÜ";
        private const string ShiftLetterGroupsUS = "0)1!2@3#4$5%6^7&8*9(`~-_=+[{]}\\|;:'\",<.>/?";

        private const string AltLetterGroupsDE = "0}2²3³7{8[9]ß\\+~<|q@e€mµ";
        private const string AltLetterGroupsUS = "4€5€";

        private const string SpecialLetterGroupsDE = "Ü^ÞäÀöºü»+¿#½-¾.¼,â<ÛßÝ´";
        private const string SpecialLetterGroupsUS = "Ü`Þ'À;º[»]¿\\½/¾.¼,â-Û-Ý=";

        private const int FirstAlphabeticLetter = 65;
        private const int LastAlphabeticLetter = 90;

        private const float KeyWaitFirstTime = 0.5f;
        private const float KeyWaitTime = 0.05f;

        private static readonly Keys[] SkipKeys = new Keys[] 
        { 
            Keys.None,
            Keys.LeftShift, Keys.RightShift, 
            Keys.LeftAlt, Keys.RightAlt,
            Keys.LeftControl, Keys.RightControl,
            //Keys.Left, Keys.Right, 
            Keys.Up, Keys.Down,
        };

        #endregion

        #region static

        private static string shiftLetterGroups = ShiftLetterGroupsUS;
        private static string altLetterGroups = AltLetterGroupsUS;
        private static string specialLetterGroups = SpecialLetterGroupsUS;
        private static TextInfo textInfo;

        #endregion

        #region Private Members
        private float waiter;
        private Keys lastPressedKey = Keys.None;

        #endregion

        static KeyboardTextInputTracker()
        {
            TextInfo = CultureInfo.CurrentUICulture.TextInfo;
        }

        public static TextInfo TextInfo
        {
            get
            {
                return textInfo;
            }

            set
            {
                textInfo = value;
                switch (textInfo.CultureName)
                {
                    case "de-DE":
                        shiftLetterGroups = ShiftLetterGroupsDE;
                        altLetterGroups = AltLetterGroupsDE;
                        specialLetterGroups = SpecialLetterGroupsDE;
                        break;
                    default:
                        shiftLetterGroups = ShiftLetterGroupsUS;
                        altLetterGroups = AltLetterGroupsUS;
                        specialLetterGroups = SpecialLetterGroupsUS;
                        break;
                }
            }
        }

        public override void Update(GameTime gameTime)
        {
            if (!this.IsTracking)
            {
                return;
            }

            KeyboardState keyState = Keyboard.GetState();

            Keys[] keys = keyState.GetPressedKeys();
            if (keys.Length == 0 || (keys.Length == 1 && keys[0] == Keys.None))
            {
                this.lastPressedKey = Keys.None;
                this.waiter = -1;
                return;
            }

            Keys k = Keys.None;

            foreach (Keys key in keys)
            {
                if (!SkipKeys.Contains(key))
                {
                    k = key;
                    break;
                }
            }

            if (k == this.lastPressedKey)
            {
                this.waiter -= (float)gameTime.ElapsedGameTime.TotalSeconds;
                if (this.waiter < 0)
                {
                    this.waiter = KeyWaitTime;
                }
                else
                {
                    return;
                }
            }
            else
            {
                this.waiter = -1;
            }

            this.lastPressedKey = k;

            bool shift = keyState.IsKeyDown(Keys.LeftShift) || keyState.IsKeyDown(Keys.RightShift);
            bool alt = keyState.IsKeyDown(Keys.RightAlt) || (keyState.IsKeyDown(Keys.LeftAlt) && keyState.IsKeyDown(Keys.LeftControl));

            switch (k)
            {
                case Keys.Back:
                    Backspace();
                    break;

                case Keys.Delete:
                    Delete();
                    break;
                case Keys.CapsLock:
                case Keys.LeftShift:
                case Keys.RightShift:
                case Keys.LeftAlt:
                case Keys.LeftControl:
                case Keys.LeftWindows:
                case Keys.RightAlt:
                case Keys.RightControl:
                case Keys.RightWindows:
                case Keys.Up:
                case Keys.Down:
                    // add all keys which should not be written
                    // (in pw mode - character without visible letters should be checked via delegate)
                    break;

                case Keys.Left:
                    --CaretPosition;
                    break;
                case Keys.Right:
                    ++CaretPosition;
                    break;
                default:

                    int ascii = (int)k;
                    string letter = ((char)ascii).ToString();

                    if (specialLetterGroups.Contains(letter[0]))
                    {
                        int idx = specialLetterGroups.IndexOf(letter[0]);

                        if (idx % 2 == 0)
                        {
                            letter = specialLetterGroups[idx + 1].ToString();
                        }
                    }

                    if (!alt && ascii >= FirstAlphabeticLetter && ascii <= LastAlphabeticLetter)
                    {
                        if (!shift)
                        {
                            letter = letter.ToLower();
                        }

                        this.AddLetter(letter[0]);
                    }
                    else
                    {
                        this.AddLetter(letter.ToLower()[0], shift, alt);
                    }

                    break;
            }

            if (this.waiter <= 0)
            {
                this.waiter = KeyWaitFirstTime;
            }
        }

        private void AddLetter(char lowerCaseLetter, bool shift, bool alt)
        {
            if (shift)
            {
                if (alt)
                {
                    return;
                }

                int idx = shiftLetterGroups.IndexOf(lowerCaseLetter) + 1;
                if (idx > 0)
                {
                    this.AddLetter(shiftLetterGroups[idx]);
                    return;
                }
            }

            if (alt)
            {
                int idx = altLetterGroups.IndexOf(lowerCaseLetter) + 1;
                if (idx > 0)
                {
                    this.AddLetter(altLetterGroups[idx]);
                    return;
                }
            }

            // add lowercase letter, if not shifted or if there is no grouping defined.
            this.AddLetter(lowerCaseLetter);
        }
    }
}
